#ifndef AdjacencyList_CPP
#define AdjacencyList_CPP

#include<iostream>
#include<limits>
#include "Queue.cpp"

using namespace std;

class AdjacencyNode
{
public:
    unsigned int vertex;
    unsigned int cost;

    AdjacencyNode(unsigned int vertex, unsigned int cost)
    {
        this->vertex = vertex;
        this->cost = cost;
    }

    void display()
    {
        cout<<"\nVertex2: "<<this->vertex<<", cost: "<<this->cost<<")\n";
    }
};

class AdjacencyListNode
{
public:
    unsigned int vertex;
    Queue<AdjacencyNode> adjacentNodes;

    AdjacencyListNode(unsigned int vertex, unsigned int vertex2, unsigned int cost)
    {
        this->vertex = vertex;
        this->adjacentNodes.enqueue(new AdjacencyNode(vertex2, cost));
        //this->adjacentNodes = new Queue<AdjacencyNode>();  //I think this is not required
    }

    bool isVertexPresent(unsigned int vertex2)
    {
        QueueNode<AdjacencyNode>* temp = this->adjacentNodes.getHead();
        while (temp != NULL)
        {
            if (temp->val->vertex == vertex2)
            {
                return true;
            }
            temp = temp->next;
        }
        return false;
    }

    void emptyIt()
    {
        while (AdjacencyNode* temp1 = this->adjacentNodes.dequeue())
        {
            delete (temp1);
        }
    }
};

class AdjacencyList
{
private:
    unsigned int maxNodeNum;

public:
    Queue<AdjacencyListNode> adjacencyListImp;

    AdjacencyList();

    bool addEdge (unsigned int vertex1, unsigned int vertex2, unsigned int cost);
    AdjacencyListNode* getAdjacencyListNode (unsigned int vertex);
    void display();
    unsigned int getNumberOfNodes();
    unsigned int** getUndirectedGraphCostMatrix(unsigned int numberOfNodes);
    QueueNode<AdjacencyListNode>* getAdjacencyListQueueNode (unsigned int vertex);
    void writeToFile();
    void emptyIt();
};

void AdjacencyList::writeToFile()
{
    ofstream outputStream;
    outputStream.open ("adjList.txt");
    
    //while (!inputStream.eof())
    //{
      //  unsigned int vertex1_uint, vertex2_uint, cost_uint;
        //char vertex1[10], vertex2[10], cost[10];
        //inputStream>>vertex1;
    
       //cout<<"adjacency List display"<<endl;
    QueueNode<AdjacencyListNode>* temp = this->adjacencyListImp.getHead();
    while (temp != NULL)
    {
        AdjacencyListNode* node = temp->val;
      //  cout<<"Vertex: "<<node->vertex;
        QueueNode<AdjacencyNode>* temp1 = node->adjacentNodes.getHead();
        while (temp1 != NULL)
        {
            AdjacencyNode* node2 = temp1->val;
            //cout<<"   ("<<node2->vertex<<", "<<node2->cost<<")";
            outputStream<<node->vertex<<" "<<node2->vertex<<" "<<node2->cost<<endl;
            temp1 = temp1->next;
        }
        cout<<endl;
        temp = temp->next;
    }
    outputStream.flush();
    outputStream.close();
}

void AdjacencyList::emptyIt()
{
    while (AdjacencyListNode* temp = this->adjacencyListImp.dequeue())
    {
        temp->emptyIt();
        delete (temp);
    }
    this->maxNodeNum = 0;
}

unsigned int** AdjacencyList::getUndirectedGraphCostMatrix(unsigned int numberOfNodes)
{
    if (this->getNumberOfNodes() > numberOfNodes)
    {
        numberOfNodes = this->getNumberOfNodes();	//should never happen just to keep this function safe
    }
    unsigned int** costMatrix = new unsigned int*[numberOfNodes];
    for (unsigned int i=0; i<numberOfNodes; i++)
    {
        costMatrix[i] = new unsigned int[numberOfNodes];
    }
    for (unsigned int i=0; i<numberOfNodes; i++)
    {
        for (unsigned int j=0; j<numberOfNodes; j++)
        {
            costMatrix[i][j] = std::numeric_limits<unsigned int>::max();;
        }
    }

    QueueNode<AdjacencyListNode>* temp = this->adjacencyListImp.getHead();
    while (temp != NULL)
    {
        AdjacencyListNode* node = temp->val;
        QueueNode<AdjacencyNode>* temp1 = node->adjacentNodes.getHead();
        while (temp1 != NULL)
        {
            AdjacencyNode* node2 = temp1->val;
            costMatrix[node->vertex][node2->vertex] = costMatrix[node2->vertex][node->vertex] = node2->cost;
            temp1 = temp1->next;
        }
        temp = temp->next;
    }
    return costMatrix;
}

unsigned int AdjacencyList::getNumberOfNodes()
{
    if (this->adjacencyListImp.hasElements())
    {
        return this->maxNodeNum + 1;
    }
    return 0;
}

void AdjacencyList::display()
{
    cout<<"adjacency List display"<<endl;
    QueueNode<AdjacencyListNode>* temp = this->adjacencyListImp.getHead();
    while (temp != NULL)
    {
        AdjacencyListNode* node = temp->val;
        cout<<"Vertex: "<<node->vertex;
        QueueNode<AdjacencyNode>* temp1 = node->adjacentNodes.getHead();
        while (temp1 != NULL)
        {
            AdjacencyNode* node2 = temp1->val;
            cout<<"   ("<<node2->vertex<<", "<<node2->cost<<")";
            temp1 = temp1->next;
        }
        cout<<endl;
        temp = temp->next;
    }
}

QueueNode<AdjacencyListNode>* AdjacencyList::getAdjacencyListQueueNode (unsigned int vertex)
{
    QueueNode<AdjacencyListNode>* temp = this->adjacencyListImp.getHead();
    while (temp != NULL)
    {
        AdjacencyListNode* node = temp->val;
        if (node->vertex == vertex)
        {
            return temp;
        }
        temp = temp->next;
    }
    return NULL;
}

AdjacencyListNode* AdjacencyList::getAdjacencyListNode (unsigned int vertex)
{
    QueueNode<AdjacencyListNode>* temp = this->adjacencyListImp.getHead();
    while (temp != NULL)
    {
        AdjacencyListNode* node = temp->val;
        if (node->vertex == vertex)
        {
            return node;
        }
        temp = temp->next;
    }
    return NULL;
}

AdjacencyList::AdjacencyList()
{
    this->maxNodeNum = 0;
    //this->adjacencyListImp = new Queue<AdjacencyListNode>();
}

bool AdjacencyList::addEdge (unsigned int vertex1, unsigned int vertex2, unsigned int cost)
{
    if (this->maxNodeNum < vertex1)
    {
        this->maxNodeNum = vertex1;
    }
    if (this->maxNodeNum < vertex2)
    {
        this->maxNodeNum = vertex2;
    }
    AdjacencyListNode* temp = this->getAdjacencyListNode(vertex1);
    if (temp == NULL)
    {
        AdjacencyListNode* node = new AdjacencyListNode(vertex1, vertex2, cost);
        this->adjacencyListImp.enqueue(node);
    }
    else
    {
        if (!temp->isVertexPresent(vertex2))
        {
            AdjacencyNode* tempNode = new AdjacencyNode(vertex2, cost);
            temp->adjacentNodes.enqueue(tempNode);
        }
        else
        {
            return false;
        }
    }
    return true;
}

#endif
